from rmasm.word_syntax import Value, Macro, Code
import rmasm.command_syntax as command
from typing import *
from rmasm.exceptions import *
from rmasm.translaters import mlog
from rmasm.translaters.f_macro_command import flattenable_macro_command_translators
from rmasm.translaters.f_macro_move_command import flattenable_macro_move_command_translators
from rmasm.translaters.macro_command import macro_command_translators
from rmasm.translaters.move_command import move_command_translators
from rmasm.utils import load_code_file


def get_mc_flatter(cmd: command.Command) -> Union[Callable[[str, List[str]], str], None]:
    """ 根据命令获取一个宏语法糖翻译器 或 None 表示该命令不是宏语法糖
    """
    if isinstance(cmd, command.MacroCommand) and \
            cmd.macro.name in flattenable_macro_command_translators:
        return flattenable_macro_command_translators[cmd.macro.name]
    return None


def get_mm_flatter(cmd: command.Command) -> Union[Callable[[str, Macro], str], None]:
    """ 根据命令获取一个宏赋值语法糖翻译器 或 None 表示该命令不是宏赋值语法糖
    """
    if isinstance(cmd, command.MoveCommand) and \
            isinstance(cmd.left, Value) and \
            isinstance(cmd.right, Macro) and \
            cmd.right.name in flattenable_macro_move_command_translators:
        return flattenable_macro_move_command_translators[cmd.right.name]
    return None


def flat_command(cmd: Union[command.Command, Code]) -> List[command.Command]:
    """ 解释一个命令的语法糖，返回多条命令。
    如果命令不是语法糖则返回一个仅有一个命令的 list。
    """

    # 空值检查
    if not cmd:
        return []
    # 如果是代码段，则调用 Translator 解析，但不翻译。
    if isinstance(cmd, Code):
        translator = Translator(source=cmd.content)
        translator.translate_stage_parse()
        if translator.errors:
            raise MultiException(errs=translator.errors)
        return translator.result_lines

    # 准备解析
    result = []

    # 解析宏命令语法糖
    fmc = get_mc_flatter(cmd)
    fmm = get_mm_flatter(cmd)
    if fmc or fmm:
        if fmc:
            rs = fmc(cmd.macro.obj, cmd.macro.args)
        if fmm:
            rs = fmm(cmd.left.value, cmd.right)
        for r in rs:
            if isinstance(r, Code):
                rc = flat_command(r)
            else:
                rc = flat_command(command.match_command(r))
            result.extend(rc)
        return result

    result.append(cmd)
    return result


def translate_move_command(cmd: command.MoveCommand) -> str:
    """ 翻译MASM层赋值语法
    """
    l = cmd.left
    r = cmd.right
    tk = (type(l), type(r))
    if tk not in move_command_translators:
        raise Exception("未知的移动操作：" + str(cmd))
    return move_command_translators[tk](l, r)


def translate_macro_command(cmd: command.MacroCommand) -> str:
    """ 翻译MASM层宏语法
    """

    m = cmd.macro
    name = m.name
    args = m.args

    if name in macro_command_translators:
        return macro_command_translators[name](m.obj, args)
    raise TranslateException("未知的宏命令：" + str(cmd.macro.name))


def translate_jump_command(cmd: command.JumpCommand, tags: Dict[str, int]) -> str:
    """ 翻译MASM层jump和end语法，需要一个表示标号的字典
    """
    # end
    if isinstance(cmd, command.EndCommand):
        return 'end'

    if cmd.target.startswith('[') and cmd.target.endswith(']'):
        target = cmd.target[1:-1].strip()
    else:
        if cmd.target not in tags:
            raise TranslateException("目标标号不存在：" + cmd.target)
        target = tags[cmd.target]

    # 有条件跳转
    if cmd.condition:
        if cmd.condition.name not in mlog.jumpJudgeOperatorMap:
            raise TranslateException("未知的条件判断运算符：" + cmd.condition.name)
        l = cmd.condition.left
        r = cmd.condition.right
        op = mlog.jumpJudgeOperatorMap[cmd.condition.name]
        return f'jump {target} {op} {l} {r}'
    # 无条件跳转
    else:
        return f'jump {target} always'


def translate_cmd(cmd: command.Command) -> Union[command.Command, str]:
    """ 翻译 MASM 层赋值、宏、原生语法，忽略jump和tag语法。
    jump和tag会原封不动的返回。

    小心这个方法会抛出各种翻译错误异常。
    """
    if isinstance(cmd, command.MoveCommand):
        return translate_move_command(cmd)
    if isinstance(cmd, command.MacroCommand):
        return translate_macro_command(cmd)
    if isinstance(cmd, command.RawCommand):
        return cmd.command
    if isinstance(cmd, command.JumpCommand):
        return cmd
    if isinstance(cmd, command.TagCommand):
        return cmd

    raise ParseException(f"尝试翻译命令时遇到了未知的命令类型: {type(cmd)}")


import logging


class Translator:
    """ R-MASM 翻译器
    """

    def __init__(self, source: str):
        """
        :param source: 源代码
        """
        self.source = source
        self.result_lines: List[Union[str, command.Command]] = []
        self.result: str = ''
        self.errors: List[str] = []
        self.tags: Dict[str, int] = {}
        self.translators: Dict[type, Callable[[command.Command]], str] = {
            command.JumpCommand: translate_jump_command,
            command.EndCommand: translate_jump_command,
            command.MacroCommand: translate_macro_command,
            command.MoveCommand: translate_move_command,
            command.RawCommand: lambda x: x.command
        }

    def add_error(self, message: str, error: Union[Exception, None]):
        """ 添加一条错误信息，参数是什么就会显示什么。
        """
        if error:
            logging.exception(error)
        self.errors.append(f'{message}')

    def translate_stage_parse(self):
        """ 进行语法解释阶段的翻译，这会处理语法糖。
        翻译出错会把错误信息存放到 errors 列表中。
        """
        lines = self.source.split('\n')
        current = ''  # 代码缓存
        depth = 0
        # 解释+翻译，完成词法解析 和 非jump\tag命令的翻译
        for k, line in enumerate(lines):
            # 判断空行、注释
            line: str = line.strip()
            if not line or line.startswith('#') or line.startswith('//'):
                continue

            current += line + '\n'
            if line.endswith('{'):
                depth += 1
            if line.startswith('}'):
                depth -= 1
            if depth != 0:
                continue

            # 下面是正式的解析
            code = current
            current = ''

            # 词法解析
            try:
                cmd = command.match_command(code)
            except Exception as e:
                self.add_error(f"[{line}] 语法错误: {e}", e)
                continue
            if not cmd:
                self.add_error(f"[{line}] 语法错误，无法理解的语句 {line}", None)
                continue

            # 宏替换
            try:
                flat_result = flat_command(cmd)
            except MultiException as e:
                [self.add_error(f'[{line}] {i}', e) for i in e.errs]
                continue
            except Exception as e:
                self.add_error(f'[{line}] 宏替换错误: {e}', e)
                continue
            self.result_lines.extend(flat_result)

    def translate_stage_translate(self):
        # 翻译
        lines = self.result_lines
        self.result_lines = []
        for cmd in lines:
            try:
                ts = translate_cmd(cmd)
                self.result_lines.append(ts)
            except Exception as e:
                self.add_error(f"翻译错误: {e}", e)
                continue

    def translate_stage_tags(self):
        """ 进行标号纪录。
        翻译出错会把错误信息存放到 errors 列表中。
        这里会忽略已翻译的语句中，#开头的代码
        """
        # 纪录 tag 命令位置，如果这里的某条命令用#开头则行号-1
        lines = self.result_lines
        self.result_lines = []
        k = 0
        for cmd in lines:
            if isinstance(cmd, command.TagCommand):
                if cmd.name in self.tags:
                    self.add_error(f"重复的标号: {cmd.name}", None)
                self.tags[cmd.name] = k
                continue
            if isinstance(cmd, str) and cmd.startswith('#'):
                k -= 1
            k += 1
            self.result_lines.append(cmd)
        pass

    def translate_stage_jump(self):
        """ 翻译标号，这需要在 translate_stage_tags 之后调用。
        翻译出错会把错误信息存放到 errors 列表中。
        """
        # 翻译 jump 命令
        lines = self.result_lines
        self.result_lines = []
        for cmd in lines:
            if isinstance(cmd, command.JumpCommand):
                try:
                    jump_str = translate_jump_command(cmd, self.tags)
                    self.result_lines.append(jump_str)
                except Exception as e:
                    self.add_error(f"跳跃指令翻译错误：{e}", None)
                continue
            if isinstance(cmd, str):
                self.result_lines.append(cmd)
                continue
            self.add_error(f"跳跃指令翻译阶段遇到了以外的内容：{cmd}", None)
        pass

    def translate(self) -> str:
        """ 翻译代码。
        这会清空错误列表、tags、已存在的翻译结果。
        返回翻译结果，并将结果存放到 result 里面。
        可能会抛出翻译错误异常
        """
        self.errors = []
        self.tags = {}
        self.result_lines = []
        self.result = ''
        self.translate_stage_parse()
        if self.errors:
            err = '\n'.join(self.errors)
            raise RmasmException(f"代码存在 {len(self.errors)} 个问题: \n{err}")
        self.translate_stage_translate()
        if self.errors:
            err = '\n'.join(self.errors)
            raise RmasmException(f"翻译阶段出现 {len(self.errors)} 个问题: \n{err}")
        self.translate_stage_tags()
        if self.errors:
            err = '\n'.join(self.errors)
            raise RmasmException(f"标号翻译出现 {len(self.errors)} 个问题: \n{err}")
        self.translate_stage_jump()
        if self.errors:
            err = '\n'.join(self.errors)
            raise RmasmException(f"Jump翻译出现 {len(self.errors)} 个问题: \n{err}")

        if len(self.result_lines) == 0 or self.result_lines[-1] != 'end':
            self.result_lines.append('end')

        self.result = '\n'.join(self.result_lines)
        return self.result
